const User = require('./../model/userModel');
const catchAsync = require('../utils/catchAsync');
const crypto = require("crypto");
const {promisify} = require('util');
const jwt = require('jsonwebtoken');
const AppError = require('./../utils/appError');
// 注册token
const signToken = id =>{
    return jwt.sign({id},process.env.JWT_SECRET,{
        expiresIn:process.env.JWT_EXPIRES_IN
    })
}
// 创建和发送token
const createSendToken = (user, statusCode,res)=>{
    const token = signToken(user._id);
    const cookieOptions = {
        expires:new Date(
            Date.now() + process.env.JWT_COOKIE_EXPIRES_IN * 24 *60 *60 *1000
        ),
        httpOnly:true,
    }
    if(process.env.NODE_ENV === 'production') cookieOptions.secure = true;
    res.cookie('jwt', token,cookieOptions);
    user.password = undefined;
    res.status(statusCode).json({
        status:"success",
        token,
        data:{
            user,
        }
    })
}

exports.signup = catchAsync(async (req, res, next) => {
    const newUser = await User.create({
        name:req.body.name,
        email:req.body.email,
        password:req.body.password,
        passwordConfirm:req.body.passwordConfirm,
        passwordChangedAt:req.body.passwordChangedAt,
        role:req.body.role
    });
    const token = jwt.sign({id:newUser._id},process.env.JWT_SECRET,{
        expiresIn:process.env.JWT_EXPIRES_IN
    })
    res.status(201).json({
        status: 'success',
        token,
        data: {
            user: newUser,
        }
    })
});

exports.login = catchAsync(async (req,res,next)=>{
    const {email,password} = req.body;
    // 1） 检查电子邮件
    if(!email || !password){
       return next(new AppError('请输入密码或者邮箱', 400))
    }
    // 2）检查用户是否存在
    const user =  await User.findOne({email}).select('+password') // 这个语法是？
    // 当用户存在 才会检验输入的密码和数据库存储的密码是否一致
    console.log(user,'查找')
    if(!user || !await user.correctPassword(password,user.password)){
        return next(new AppError('请输入密码或者邮箱', 401))
    }
    // 3） 如果用户存在则发送token
    createSendToken(user,200,res)
    // const token = signToken(user._id);
    // res.status(200).json({
    //     status:'success',
    //     token
    // })

})
// 路由权限保护
exports.protect = catchAsync(async (req,res,next)=>{
    // 1) 检验token是否有存在
    let token ;
    if(req.headers.authorization && req.headers.authorization.startsWith('Bearer')){
        token = req.headers.authorization.split(' ')[1];
    }
    //console.log(token);
    if(!token){
        return next(new AppError('请登录获取更多权限', 401));
    }
    // 2) 验证token
    const decoded = await promisify(jwt.verify)(token, process.env.JWT_SECRET);
    //console.log(decoded,'这是啥')

    // 3) 检验用户是否仍然存在
    const freshUser = await User.findById(decoded.id);
    if(!freshUser){
        return next(
            new AppError('这个token的用户已经被删除了',401)
        )
    }
    // 40 用户在token合法后 改变密码
    if(freshUser.changesPasswordAfter(decoded.iat)){
        return next(
            new AppError('用户密码被更改 请重新登录',401)
        )
    };
    //console.log(freshUser,'查找用户')
    req.user = freshUser;
    next();
})

// 权限检查中间件 传入白名单权限 看是否有权限
exports.restrictTo = (...roles)=>{
    return(req,res,next)=>{
        // roles 是一个数组 默认值是['admin','lead-guide']
        if(!roles.includes(req.user.role)){
            return next(new AppError('你并没有权限',403))
        }
        next();
    }
    
}

// 忘记密码
exports.forgotPassword = catchAsync(async(req,res,next)=>{
    // 1) 获取用户提交的email 账号
    const user = await User.findOne({email:req.body.email});
    if(!user){
        return next(new AppError('未找到用户',403))
    }
    // 2) 创建一个随机的重置token 废弃之前的token
    const resetToken = user.createPasswordResetToken();
    await user.save({validateBeforeSave:false}); // 这步撤销所有校检器
    res.status(200).json({
        status:'success',
        resetToken
    })
}) 

// 重置密码
exports.resetPassword = catchAsync(async(req,res,next)=>{
    // 1 根据token获取用户
    const hashedToken = crypto.createHash('sha256').update(req.params.token).digest('hex');
    const user = await User.findOne({
        passwordResetToken:hashedToken,
        passwordResetExpires:{$gt:Date.now()}
    });
    // 2 token没过期就设置新的密码
    if(!user){
        return next(new AppError('token不合法或者过期了',400))
    }
    user.password = req.body.password;
    user.passwordConfirm = req.body.passwordConfirm;
    user.passwordResetToken = undefined;
    user.passwordResetExpires = undefined;
    await user.save();
    // 3 更新密码改变字段
    // 4
    const token = signToken(user._id);
    res.status(200).json({
        status:'success',
        token
    })
}) 
// 更新密码
exports.updatePassword = catchAsync(async(req,res,next)=>{
    // 1. 从集合中查找出该用户
    // 要求返回的对象只要password字段 User来自protect中间件 检测token身份权限的
    const user = await User.findById(req.user.id).select('+password');
    console.log(user)
    // 2. 检查他的密码是否正确
    if(!(await user.correctPassword(req.body.passwordCurrent,user.password))){
        return next(new AppError('密码错误',401))
    }
    // 3. 正确则更新密码
    user.password = req.body.password;
    user.passwordConfirm = req.body.passwordConfirm;
    await user.save(); // 不使用更新是因为无法触发钩子函数
    // 4. 记录user 发送token  没必要 基本都是重定向走了 返回401
    createSendToken(user,200,res)
})